﻿/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "World.h"
#include <string.h>
#include <NodeCpp/AutoPtr.h>
#include <NodeCpp/Thread.h>
#include "Shared/Log/Log.h"
#include "GlobalVars.h"
#include "Game/SharedDefines.h"
#include "Game/DBCStores.h"
#include "Game/ObjectManager.h"
#include "Server/WorldSession.h"
#include "Server/PacketFilter.h"
using NodeCpp::AutoPtr;
using NodeCpp::Thread;

World::World(void)
    : NewSessions_()
    , QueuedSessions_()
    , Sessions_()
    , Running_(true)
{
    memset(&ConfigUInt32_, 0, sizeof(ConfigUInt32_));
    memset(&ConfigFloat_, 0, sizeof(ConfigFloat_));
}

World::~World(void)
{

}

void World::InitializeVars(const struct Configuration::Any& _WorldConf)
{
    /********************* UINT32 VALUES *********************/
    SetConfig(CONFIG_UINT32_VALUE_REALM_ID, _WorldConf["RealmID"].As<std::uint32_t>());
    SetConfig(CONFIG_UINT32_VALUE_PLAYER_LIMIT, _WorldConf["PlayerLimit"].As<std::uint32_t>()); 

    SetConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL, _WorldConf["MaxPlayerLevel"].As<std::uint32_t>());
    SetConfig(CONFIG_UINT32_CHARACTERS_CREATING_DISABLED, _WorldConf["CharacterCreateTeamLimit"].As<std::uint32_t>());
}

bool World::LoadDBCs(const struct Configuration::Any& _WorldConf)
{
    return sDBCStores.LoadDBCs(_WorldConf["DBCPath"].As<std::string>());
}

bool World::InitializeWorld(const struct Configuration::Any& _WorldConf)
{
    bool _LoadOk = sObjMgr.LoadCreatureTemplates();
    _LoadOk &= sObjMgr.LoadInstanceTemplate();
    _LoadOk &= sObjMgr.LoadItemPrototypes();

    _LoadOk &= sObjMgr.LoadPlayerInfo();
    return _LoadOk;
}

void World::Update(time_t _Diff)
{
    // 更新会话
    _UpdateSessions();
}

void World::_UpdateSessions(void)
{
    /* 将新会话加入会话表，并将会话进入游戏世界或排队队列 */
    WorldSession* _NewSession;
    while(NewSessions_.Next(_NewSession)) {
        _AddSession(_NewSession);
    }

    /* 更新会话表中所有会话 */
    SessionMap::const_iterator _Iter = Sessions_.begin();
    for (; _Iter != Sessions_.end();)
    {
        WorldSession* _Session = _Iter->second;
        GameSessionFilter _Filter(_Session);
        if (!_Session->Update(_Filter))
        {
            /* 有会话关闭 */
            _Iter = Sessions_.erase(_Iter);
            if (_RemoveQueuedSession(_Session)) {
                _UpdatePopulation();
            }
            SAFE_DEL(_Session);
            continue;
        }
        ++_Iter;
    }
}

void World::Close(void)
{
    /* 等待所有会话关闭 */
    const time_t WaitTime = 20;
    Running_ = false;
    while (!(Sessions_.empty() && NewSessions_.Empty())) {
        Update(WaitTime);
        Thread::Sleep(WaitTime);
    }
}

WorldSession* World::FindSession(std::uint32_t _AccountId)
{
    SessionMap::const_iterator _Iter = Sessions_.find(_AccountId);
    if (_Iter != Sessions_.end()) {
        return _Iter->second;
    }
    return NULL;
}

void World::_AddSession(WorldSession* _Session)
{
    NODECPP_ASSERT(_Session);
    Sessions_[_Session->GetAccountId()] = _Session;

    /* 判断玩家是否需要排队 */
    if (_Session->GetSecurity() == SEC_PLAYER) {
        std::uint32_t _Limit = GetConfig(CONFIG_UINT32_VALUE_PLAYER_LIMIT);
        std::uint32_t _Sessions = GetActiveAndQueuedSessionCount();
        if ((_Limit > 0) && (_Sessions > _Limit)) {
            _AddQueuedSession(_Session);
            sLog.outDetail("Account id %u is in Queue Position (%u).",
                _Session->GetAccountId(), GetQueuedSessionCount() + 1);
            return;
        }
    }

    _Session->SendAuthOk();
    _UpdatePopulation();
}

void World::_AddQueuedSession(WorldSession* _Session)
{
    // 将会话加入排队队列
    QueuedSessions_.push_back(_Session);
    _Session->SendAuthWaitQue(_GetQueuedSessionPos(_Session));
}

bool World::_RemoveQueuedSession(WorldSession* _Session)
{
    // 当游戏世界中有玩家数量变化时才需要更新服务器热度
    bool _NeedUpdatePopulation = false;
    // 该会话是否从排队队列移除
    bool _InQueue = false;
    // 该会话在队列中的位置
    std::uint32_t _Postion = 1;

    /* 将会话从排队队列移除 */
    SessionQeueu::iterator _Iter = QueuedSessions_.begin();
    for (; _Iter != QueuedSessions_.end();) {
        if ((*_Iter) == _Session) {
            // 将_Iter指向队列该会话后面的会话，因为要更新这些会话的队列位置
            _Iter = QueuedSessions_.erase(_Iter);
            _InQueue = true;
            break;
        }
        ++_Iter;
        ++_Postion;
    }

    if (!_InQueue)
    {
        /* 该会话是从游戏世界移除的 */
        std::uint32_t _Limit = GetConfig(CONFIG_UINT32_VALUE_PLAYER_LIMIT);
        std::uint32_t _ActiveSessions = GetActiveSessionCount();

        if ((_Limit > 0)
            && (_ActiveSessions < _Limit)
            && (!QueuedSessions_.empty()))
        {
            /* 将队列第一个玩家加入游戏世界 */
            WorldSession *_Sess = QueuedSessions_.front();
            _Sess->SendAuthWaitQue(0);
            QueuedSessions_.pop_front();

            _Iter = QueuedSessions_.begin();
            _Postion = 1;
        }
        else {
            _NeedUpdatePopulation = true;
        }
    }

    /* 更新队列中玩家位置 */
    for (; _Iter != QueuedSessions_.end(); ++_Iter, ++_Postion) {
        (*_Iter)->SendAuthWaitQue(_Postion);
    }

    return _NeedUpdatePopulation;
}

std::uint32_t World::_GetQueuedSessionPos(WorldSession* _Session)
{
    std::uint32_t _Position = 1;
    for (SessionQeueu::const_iterator _Iter = QueuedSessions_.begin();
        _Iter != QueuedSessions_.end(); ++_Iter, ++_Position) {
            if ((*_Iter) == _Session) {
                return _Position;
            }
    }
    return 0;
}

void World::_UpdatePopulation(void)
{
    /* 更新服务器热度 */
    std::uint32_t _Limit = GetConfig(CONFIG_UINT32_VALUE_PLAYER_LIMIT);
    if (_Limit > 0)
    {
        float _Popu = float(GetActiveSessionCount());
        _Popu /= _Limit;
        _Popu *= 2;

        static SqlStatementID _UpdatePopulation;
        SqlStatement _Stmt = sLoginDB.CreateStatement(_UpdatePopulation, "UPDATE realmlist SET population = ? WHERE id = ?");
        _Stmt.PExecute(_Popu, GetConfig(CONFIG_UINT32_VALUE_REALM_ID));

        sLog.outDetail("Server Population (%f).", _Popu);
    }
}
